package org.jader.bolao.statistics;

import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import org.jader.bolao.Championship;
import org.jader.bolao.Game;
import org.jader.bolao.GamePlayer;
import org.jader.bolao.Player;
import org.jader.bolao.Team;
import org.jader.bolao.gui.Application;

import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;

public class TeamStatistics {
	
	private List<TSTeam> teams = new LinkedList<TSTeam>();
	
	private TeamStatistics(){}
	
	public static TeamStatistics generate(Championship c) {
		TeamStatistics ts = new TeamStatistics();
		
		ts.copyTeams(c);
		ts.orderByPlayerName();
		
		if (Application.DEBUG_MODE) System.out.println("TeamStatistics::generate() => Teams: "+ts.numberOfTeams()+"  Max. games per Player: "+ts.maxGamesPerPlayer());
		return ts;
	}
	
	private void copyTeams(Championship c) {
		Iterator<org.jader.bolao.Team> itt = c.teamsIterator();
		while (itt.hasNext()) {
			TSTeam team = new TSTeam(itt.next()); // Local team copy
			this.teams.add(team);
		}
	}
	
	public void orderByPlayerName() {
		Iterator<TSTeam> itt = this.teams.iterator();
		while (itt.hasNext()) {
			TSTeam team = itt.next();
			for (int i=0; i<team.numberOfPlayers(); i++) {
				for (int j=i+1; j<team.numberOfPlayers(); j++) {
					if (team.players.get(i).player().getName().compareTo(team.players.get(j).player().getName()) > 0) {
						TSPlayer temp = team.getPlayer(i);
						team.players.set(i, team.players.get(j));
						team.players.set(j, temp);
					}
				}
			}
		}
	}

	public int maxGamesPerPlayer() {
		int maxGames = 0;
		Iterator<TSTeam> itt = teams.iterator();
		while (itt.hasNext()) {
			Iterator<TSPlayer> itp = itt.next().players.iterator();
			while (itp.hasNext()) {
				TSPlayer p = itp.next();
				if (p.numberOfPoints() > maxGames)
					maxGames = p.numberOfPoints();
			}
		}
		return maxGames;
	}
	
	public TSTeam getTeam(int arg0) {
		return teams.get(arg0);
	}

	public Iterator<TSTeam> teamsIterator() {
		return teams.iterator();
	}

	public int numberOfTeams() {
		return teams.size();
	}

	public class TSPlayer {
		private Player player = null;
		private List<Game> games = new LinkedList<Game>();
		private Float averange = new Float(0);
		private Integer totalPoints = 0;
		
		private TSPlayer(Player p) {
			this.player = p;
			// Copy only games here player plays
			Iterator<Game> it = p.team().championship().gamesIterator();
			while (it.hasNext()) {
				Game g = it.next();
				if (g.containsPlayer(this.player)) {
					this.games.add(g);
				}
			}
			refreshAvgAndTotal();
		}
		
		public Player player() {return this.player;}

		@Override
		public boolean equals(Object arg0) {
			if (arg0 instanceof Player) {
				Player p = (Player) arg0;
				return this.player.equals(p);
			}
			return super.equals(arg0);
		}

		private void refreshAvgAndTotal() {
			int total = 0;
			int gameCounter = 0;
			Iterator <Game>it = games.iterator();
			while (it.hasNext()) {
				GamePlayer gp = it.next().getPlayer(this.player);
				if (gp != null && gp.getSum() > 0) {
					total += gp.getSum();
					gameCounter++;
				}
			}
			if (gameCounter > 0) {
				this.totalPoints = total;
				this.averange = new Float((float)total / (float)gameCounter);
			}
		}
		
		public int numberOfPoints() {
			return games.size();
		}

		public Integer getTotalSum() {return this.totalPoints;}
		
		public Float getAverange() {return this.averange;}
		
		public Game getGame(int index) {
			return this.games.get(index);
		}
	}
	
	public class TSTeam {
		private Team team = null;
		private List<TSPlayer> players = new LinkedList<TSPlayer>();
		
		private TSTeam (Team t) {
			this.team = t;
			// Initialize players
			Iterator<Player> itp = t.playerIterator();
			while (itp.hasNext()) players.add(new TSPlayer(itp.next()));
		}
		
		public Team team() {return this.team;}

		public boolean containsPlayer(Object arg0) {
			return players.contains(arg0);
		}

		public TSPlayer getPlayer(int arg0) {
			return players.get(arg0);
		}

		public boolean hasNoPlayer() {
			return players.isEmpty();
		}

		public Iterator<TSPlayer> playerIterator() {
			return players.iterator();
		}

		public int numberOfPlayers() {
			return players.size();
		}

	}
	
	public static class TSXMLConverter implements Converter {

		@Override
		public void marshal(Object arg0, HierarchicalStreamWriter writer,
				MarshallingContext context) {
			// Initialize formatters
			SimpleDateFormat dateFormatter = new SimpleDateFormat("dd/MM/yyyy");
			DecimalFormat numberFormatter = new DecimalFormat();
			numberFormatter.setMaximumFractionDigits(3);
			// Get TeamStatistics element
			TeamStatistics ts = (TeamStatistics) arg0;
			// Generate complete statistics xml
			writer.startNode("teams");
			Iterator<TSTeam> itt = ts.teamsIterator();
			while (itt.hasNext()) {
				TSTeam tst = itt.next();
				writer.startNode("team");
				
				writer.startNode("name");
				writer.setValue(tst.team().getName());
				writer.endNode(); //name
				
				writer.startNode("players");
				Iterator<TSPlayer> itp = tst.playerIterator();
				while (itp.hasNext()) {
					TSPlayer tsp = itp.next();
					writer.startNode("player");

					writer.startNode("name");
					writer.setValue(tsp.player().getName());
					writer.endNode(); //name
					
					writer.startNode("points");
					Iterator<Game> itg = tsp.games.iterator();
					while (itg.hasNext()) {
						Game g = itg.next();
						writer.startNode("point");
						
						writer.startNode("sum");
						writer.setValue(Integer.toString(g.getPlayer(tsp.player()).getSum()));
						writer.endNode(); //sum
						
						writer.startNode("game");
						
						writer.startNode("place");
						writer.setValue(g.getPlace());
						writer.endNode(); //place
						
						writer.startNode("date");
						writer.setValue(dateFormatter.format(g.getDate()));
						writer.endNode(); //date
						
						writer.endNode(); //game
						
						writer.endNode(); //point
					}
					writer.endNode(); //points
					
					writer.startNode("averange");
					writer.setValue(numberFormatter.format(tsp.getAverange()));
					writer.endNode(); //averange
					
					writer.startNode("totalPoints");
					writer.setValue(tsp.getTotalSum().toString());
					writer.endNode(); //totalPoints
					
					writer.endNode(); //player
				}
				writer.endNode(); //players
				
				writer.endNode(); //team
			}
			
			writer.endNode(); //teams
		}

		@Override
		public Object unmarshal(HierarchicalStreamReader arg0,
				UnmarshallingContext arg1) {
			// TeamStatistics are not recovered from XML
			return null;
		}

		@SuppressWarnings("unchecked")
		@Override
		public boolean canConvert(Class arg0) {
			return arg0.equals(TeamStatistics.class);
		}

	}
}
